package com.yzh.dbrouter.util;

import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertyResolver;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @description: 属性工具类
 */
public class PropertyUtil {
    
    private static int springBootVersion = 1;
    //判断SpringBoot版本，如果是1.x版本会有RelaxedPropertyResolver类，若获取不到说明是2.x版本
    static {
        try {
            Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver");
        } catch (ClassNotFoundException e) {
            springBootVersion = 2;
        }
    }

    /**
     * 根据SpringBoot版本调用不同的方法
     */
    @SuppressWarnings("unchecked")
    public static <T> T handle(final Environment environment, final String prefix, final Class<T> targetClass) {
        switch (springBootVersion) {
            case 1:
                return (T) v1(environment, prefix);
            default:
                return (T) v2(environment, prefix, targetClass);
        }
    }

    private static Object v1(final Environment environment, final String prefix) {
        try {
            Class<?> resolverClass = Class.forName("org.springframework.boot.bind.RelaxedPropertyResolver");
            Constructor<?> resolverConstructor = resolverClass.getDeclaredConstructor(PropertyResolver.class);
            Method getSubPropertiesMethod = resolverClass.getDeclaredMethod("getSubProperties", String.class);
            Object resolverObject = resolverConstructor.newInstance(environment);
            String prefixParam = prefix.endsWith(".") ? prefix : prefix + ".";
            return getSubPropertiesMethod.invoke(resolverObject, prefixParam);
        } catch (final ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
                | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    /**
     * 使用 org.springframework.boot.context.properties.bind.Binder 类的 get 方法获取一个 Binder 对象
     * 然后使用 bind 方法将 prefix 和 targetClass 参数绑定，并获取绑定后的结果对象。最后通过结果对象的 get 方法获取结果并返回
     */
    private static Object v2(final Environment environment, final String prefix, final Class<?> targetClass) {
        try {
            //Binder是将配置文件和对象绑定的类
            Class<?> binderClass = Class.forName("org.springframework.boot.context.properties.bind.Binder");
            Method getMethod = binderClass.getDeclaredMethod("get", Environment.class);
            Method bindMethod = binderClass.getDeclaredMethod("bind", String.class, Class.class);
            //通过 get 方法获取 Binder 实例，参数为传入的 Environment 对象。第一个参数需要传入对象实例，因为get是静态方法所以为null
            Object binderObject = getMethod.invoke(null, environment);
            //处理 prefix 参数，确保它不以 "." 结尾
            String prefixParam = prefix.endsWith(".") ? prefix.substring(0, prefix.length() - 1) : prefix;
            //通过 bind 方法绑定属性，传入 prefix 和 targetClass
            Object bindResultObject = bindMethod.invoke(binderObject, prefixParam, targetClass);
            //获取绑定结果对象中的 get 方法，该方法无参数
            Method resultGetMethod = bindResultObject.getClass().getDeclaredMethod("get");
            //调用 get 方法获取最终的绑定结果
            return resultGetMethod.invoke(bindResultObject);
        }
        catch (final ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException
                | IllegalArgumentException | InvocationTargetException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

}
